moapyrfandomcom-20200222-history
User blog:PACERRECAP/Garrison ROF Calculation and Best Practice
Introduction First of all, I'd like to thank AlexB whose advices and insights greatly assisted this research. Garrisoning has always been a significant mechanism in Red Alert 2. Although easy to comprehend, the precise numeric value is much harder to calculate than anticipated. Few documentation can be found on this topic, moreover these hardly ever fit to test results. This blog will go deep and deduce the precise formula. In this blog we're talking about infantry garrisoning only. Tank Bunker's ROF is trivial. The problem During gameplay, one might notice that each unit in a building takes its turn to fire, regardless of their respective ROFs. For example, even though a GI fires almost three times faster than a Knightframe, garrisoning them inside the same building will make them fire in turn, rather than at a 3:1 rate. It's also easy to observe that each unit uses its own ROF during said turn. The more units garrisoned inside a building, the faster each one of them fires. The ROF is affected by the following: * ROF of the weapon fired: ROF * Number of occupiers * Universal garrisoning ROF Bonus: OccupyROFMultiplier = 1.2 * Veterancy ROF bonus: ROF = 1.0 * ROF bonus provided by difficulty: ROF = 1.0 for all * Subfaction-wise weapon ROF bonus: ROF = 1.0 for all In both YR and MO the last three factors are always 1.0 and thus neglected below. One might guess that the garrisoning ROF would be something close to this: ROF = WeaponROF / OccupyROFMultiplier / NumOccupiers However no matter if it's rounded or floored (the ROF in frames is always integer), this formula is not quite close to the test results. In fact, garrison ROF is one of the most whimsical mechanism one can encounter in usual games. After several (tens of) attempts, to read the disassembled code directly seems the only choice remaining. Tools gamemd.exe is the game engine which implements the garrisoning logic. In order to put ARES' new logics into use, MO 3.3 uses syringe.exe to inject ARES' DLL into the engine. Doing so prevents another debugger from attaching. We're therefore forced to debug vanilla YR instead. As of ARES version 2.0, there's no hook in this section of the code. Therefore it's safe to say that YR's algorithm still applys to MO 3.3. We're using IDA for static analysis, and Ollydbg for debugging. The frmula With a debugger up and running, reading the ASM is no longer a difficult task. It appears that the game checks for garrison firing every two frames, while performing a check at the next frame after a shot (so that a ROF=1 weapon won't be terribly hampered). The ROF calculation itself is inside function 6FCFA0, which also generates a random number (chosen at equal probability) on the run. The algorithm is summarized below: A = float(WeaponROF) * SubfactionROFBonus + 0/1/2 B = TruncateToInt(A / NumOccupiers) C = TruncateToInt(float(B) / OccupyROFMultiplier) ROF = MAX(C, 1) Note that truncating float to int might lead to unexpected floorings: 20 / 4 = 4.999999... = 4 This is the principal reason why all previous assumptions failed. In addition to this, firing is checked per two frames except for the first frame. Which means even ROFs will be delayed for one frame. And there comes the formula, which corresponds perfectly to all test cases be it in YR and MO. For instance, a Conscript (ROF=24) alone in a building fires once per 19 frames when the random factor is 0, and 21 frames when the factor is 1 or 2. The average ROF is therefore (19 + 21 + 21) / 3 = 20.33. Calculating the precise ROF may help a commander better organize resources at hand, optimizing the defenses with limited manpower. Best practice Power underestimated An effect more than often overlooked before it's too late, garrisoned buildings fires significantly quicker than all the units added together when outside. For example, by garrisoning a civilian building with 9 Conscripts, each of them fires 24 times faster than on the ground. Combined with a 33% damage bonus and 1.5 tiles addition range. Such structure becomes a terrible infantry killer, at $450. 1 + 1 < 2 Since every single weapon fires quicker with more garrison inside the building, it's trivial that soldiers works much better when together. 6 GIs performs nearly twice as well as 3+3 added together. If not competing for building ownership, it's almost always better to focus on chokepoints than spreading the troops. For the same reason, completely filling Battle Bunkers is strongly advised. Ceiling exists The algorithm dictates that the ROFs often increases at an cascading rate. Certain infantry compositions bring no benefit to firepower. For example, 5 garrisoned Flak Trooper fires exactly as fast as 4 of them. Memorizing these 'no's may be the edge a commander need in a long snowballing game. Moreover, every weapon's ROF has a hard cap of 1 frame, which makes filling Tech Concrete Fortresses with 20 soldiers absolutely unnecessary for any faction, unless the commander is so eager to micro-adjust the anti-infantry-anti-armor ratio. Trivia * Knightframe's occupy weapon reuses Buzzard's firing animation. Since buzzard has two guns, Knightframe will look like firing twice in each burst, but it only ever fires once. * Malver fires as fast as an ordinary Initiate, although he applys an AoE suppression effect instead of dealing real damage. Category:Blog posts